﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
using System.Text;

namespace PmSoft.Core.Cryptos
{
	/// <summary>
	/// 提供 RSA 加密和解密的工具类，支持公钥加密、私钥加密以及分段处理长数据。
	/// </summary>
	public static class RsaUtility
	{
		/// <summary>
		/// 使用私钥加密数据（基于 BouncyCastle 的 PKCS1 填充）。
		/// </summary>
		/// <param name="data">要加密的字符串</param>
		/// <param name="privateKey">私钥（支持多种格式，默认为 PKCS8）</param>
		/// <param name="keyFormat">私钥格式，默认为 "PKCS8"</param>
		/// <returns>加密后的字节数组</returns>
		/// <exception cref="ArgumentNullException">当 data 或 privateKey 为 null 时抛出</exception>
		public static byte[] EncryptWithPrivateKey(string data, string privateKey, string keyFormat = "PKCS8")
		{
			ArgumentNullException.ThrowIfNull(data, nameof(data));
			ArgumentNullException.ThrowIfNull(privateKey, nameof(privateKey));

			RSACryptoServiceProvider rsa = RsaKeyUtility.LoadPrivateKey(privateKey.Trim(), keyFormat);
			AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetKeyPair(rsa);
			IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");
			cipher.Init(true, keyPair.Private);
			byte[] dataBytes = Encoding.UTF8.GetBytes(data);
			return cipher.DoFinal(dataBytes);
		}

		/// <summary>
		/// 使用公钥解密数据（基于 BouncyCastle 的 PKCS1 填充）。
		/// </summary>
		/// <param name="encryptedData">Base64 编码的加密数据</param>
		/// <param name="publicKey">公钥（Java 格式）</param>
		/// <returns>解密后的字节数组</returns>
		/// <exception cref="ArgumentNullException">当 encryptedData 或 publicKey 为 null 时抛出</exception>
		public static byte[] DecryptWithPublicKey(string encryptedData, string publicKey)
		{
			ArgumentNullException.ThrowIfNull(encryptedData, nameof(encryptedData));
			ArgumentNullException.ThrowIfNull(publicKey, nameof(publicKey));

			string xmlPublicKey = ConvertJavaPublicKeyToDotNetXml(publicKey.Trim());
			using RSACryptoServiceProvider rsa = new();
			rsa.FromXmlString(xmlPublicKey);
			AsymmetricKeyParameter keyPair = DotNetUtilities.GetRsaPublicKey(rsa);
			IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");
			cipher.Init(false, keyPair);
			byte[] dataBytes = Convert.FromBase64String(encryptedData);
			return cipher.DoFinal(dataBytes);
		}

		/// <summary>
		/// 使用公钥加密数据，支持长数据分段处理。
		/// </summary>
		/// <param name="data">要加密的字符串</param>
		/// <param name="publicKey">公钥</param>
		/// <param name="charset">字符编码，默认为 "UTF-8"</param>
		/// <returns>Base64 编码的加密数据</returns>
		/// <exception cref="ArgumentNullException">当 data 或 publicKey 为 null 时抛出</exception>
		public static string EncryptWithPublicKey(string data, string publicKey, string charset = "UTF-8")
		{
			ArgumentNullException.ThrowIfNull(data, nameof(data));
			ArgumentNullException.ThrowIfNull(publicKey, nameof(publicKey));

			using RSACryptoServiceProvider rsa = RsaKeyUtility.LoadPublicKey(publicKey);
			byte[] dataBytes = Encoding.GetEncoding(charset).GetBytes(data);
			byte[] encryptedBytes = EncryptLongData(rsa, dataBytes);
			return Convert.ToBase64String(encryptedBytes, Base64FormattingOptions.None);
		}

		/// <summary>
		/// 使用私钥解密数据，支持长数据分段处理。
		/// </summary>
		/// <param name="encryptedData">Base64 编码的加密数据</param>
		/// <param name="privateKey">私钥</param>
		/// <param name="keyFormat">私钥格式，默认为 "PKCS8"</param>
		/// <param name="charset">字符编码，默认为 "UTF-8"</param>
		/// <returns>解密后的字符串</returns>
		/// <exception cref="ArgumentNullException">当 encryptedData 或 privateKey 为 null 时抛出</exception>
		public static string DecryptWithPrivateKey(string encryptedData, string privateKey, string keyFormat = "PKCS8", string charset = "UTF-8")
		{
			ArgumentNullException.ThrowIfNull(encryptedData, nameof(encryptedData));
			ArgumentNullException.ThrowIfNull(privateKey, nameof(privateKey));

			using RSACryptoServiceProvider rsa = RsaKeyUtility.LoadPrivateKey(privateKey.Trim(), keyFormat);
			byte[] encryptedBytes = Convert.FromBase64String(encryptedData);
			byte[] decryptedBytes = DecryptLongData(rsa, encryptedBytes);
			return Encoding.GetEncoding(charset).GetString(decryptedBytes);
		}

		/// <summary>
		/// 将 Java 格式的 RSA 公钥转换为 .NET XML 格式。
		/// </summary>
		private static string ConvertJavaPublicKeyToDotNetXml(string publicKey)
		{
			RsaKeyParameters keyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
			return $"<RSAKeyValue><Modulus>{Convert.ToBase64String(keyParams.Modulus.ToByteArrayUnsigned())}</Modulus><Exponent>{Convert.ToBase64String(keyParams.Exponent.ToByteArrayUnsigned())}</Exponent></RSAKeyValue>";
		}

		/// <summary>
		/// 对长数据进行分段加密。
		/// </summary>
		private static byte[] EncryptLongData(RSACryptoServiceProvider rsa, byte[] data)
		{
			int maxBlockSize = rsa.KeySize / 8 - 11; // PKCS1 填充的最大块大小
			if (data.Length <= maxBlockSize)
			{
				return rsa.Encrypt(data, fOAEP: false);
			}

			using MemoryStream inputStream = new(data);
			using MemoryStream outputStream = new();
			byte[] buffer = new byte[maxBlockSize];
			int bytesRead;

			while ((bytesRead = inputStream.Read(buffer, 0, maxBlockSize)) > 0)
			{
				byte[] block = buffer[..bytesRead];
				byte[] encryptedBlock = rsa.Encrypt(block, fOAEP: false);
				outputStream.Write(encryptedBlock);
			}

			return outputStream.ToArray();
		}

		/// <summary>
		/// 对长数据进行分段解密。
		/// </summary>
		private static byte[] DecryptLongData(RSACryptoServiceProvider rsa, byte[] data)
		{
			int maxBlockSize = rsa.KeySize / 8; // 解密块大小等于密钥大小
			if (data.Length <= maxBlockSize)
			{
				return rsa.Decrypt(data, fOAEP: false);
			}

			using MemoryStream inputStream = new(data);
			using MemoryStream outputStream = new();
			byte[] buffer = new byte[maxBlockSize];
			int bytesRead;

			while ((bytesRead = inputStream.Read(buffer, 0, maxBlockSize)) > 0)
			{
				byte[] block = buffer[..bytesRead];
				byte[] decryptedBlock = rsa.Decrypt(block, fOAEP: false);
				outputStream.Write(decryptedBlock);
			}

			return outputStream.ToArray();
		}
	}
}